{{!

  Copyright (c) Meta Platforms, Inc. and affiliates.

  Licensed under the Apache License, Version 2.0 (the "License");
  you may not use this file except in compliance with the License.
  You may obtain a copy of the License at

      http://www.apache.org/licenses/LICENSE-2.0

  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.

}}{{#service:functions?}}
//
// Service Methods
//

{{/service:functions?}}
{{#service:functions}}{{#function:return_type}}{{^function:starts_interaction?}}
//
// Method '{{function:name}}'
//
template <typename ProtocolIn_, typename ProtocolOut_>
void {{service:parent_service_cpp_name}}AsyncProcessor::setUpAndProcess_{{function:prefixed_name}}(
    apache::thrift::ResponseChannelRequest::UniquePtr req,
    apache::thrift::SerializedCompressedRequest&& serializedRequest,
    apache::thrift::Cpp2RequestContext* ctx,
    folly::EventBase* eb,
    [[maybe_unused]] apache::thrift::concurrency::ThreadManager* tm) {
  if (!setUpRequestProcessing(
          req, ctx, eb, {{^function:eb}}tm{{/function:eb}}{{#function:eb}}nullptr{{/function:eb}}, {{> types/function_kind}}, iface_{{#service:interaction?}}, "{{service:name}}"{{/service:interaction?}}{{#function:creates_interaction?}}, "{{function:created_interaction}}", true{{/function:creates_interaction?}})) {
    return;
  }
{{^function:eb}}
  auto scope = iface_->getRequestExecutionScope(
      ctx, apache::thrift::concurrency::{{function:priority}});
  ctx->setRequestExecutionScope(std::move(scope));
  processInThread(
      std::move(req),
      std::move(serializedRequest),
      ctx,
      eb,
      tm,
      {{> types/function_kind}},
      &{{service:parent_service_cpp_name}}AsyncProcessor::
          executeRequest_{{function:prefixed_name}}<ProtocolIn_, ProtocolOut_>,
      this);
{{/function:eb}}{{#function:eb}}
  {{#function:in_or_creates_interaction?}}
  processInThread(
      std::move(req),
      std::move(serializedRequest),
      ctx,
      eb,
      nullptr,
      {{> types/function_kind}},
      &{{service:parent_service_cpp_name}}AsyncProcessor::
          executeRequest_{{function:prefixed_name}}<ProtocolIn_, ProtocolOut_>,
      this);
  {{/function:in_or_creates_interaction?}}
  {{^function:in_or_creates_interaction?}}
  {{^function:oneway?}}
  if (!req->getShouldStartProcessing()) {
    {{#function:in_or_creates_interaction?}}
    auto tile = serverRequest.requestContext()->releaseTile();
    {{/function:in_or_creates_interaction?}}
    apache::thrift::HandlerCallbackBase::releaseRequest(
        std::move(req),
        eb{{#function:in_or_creates_interaction?}},
        std::move(tile){{/function:in_or_creates_interaction?}});
    return;
  }
  {{/function:oneway?}}
  apache::thrift::ServerRequest serverRequest{
      std::move(req),
      std::move(serializedRequest),
      ctx,
      {},
      {},
      {},
      {}};
  executeRequest_{{function:prefixed_name}}<ProtocolIn_, ProtocolOut_>(std::move(serverRequest));
  {{/function:in_or_creates_interaction?}}
{{/function:eb}}
}

template <typename ProtocolIn_, typename ProtocolOut_>
void {{service:parent_service_cpp_name}}AsyncProcessor::executeRequest_{{#service:interaction?}}{{service:name}}_{{/service:interaction?}}{{function:cpp_name}}(
    apache::thrift::ServerRequest&& serverRequest) {
{{#function:in_or_creates_interaction?}}
  auto tile = serverRequest.requestContext()->releaseTile();
{{/function:in_or_creates_interaction?}}
  // make sure getRequestContext is null
  // so async calls don't accidentally use it
  iface_->setRequestContext(nullptr);
  struct ArgsState {
{{#function:args}}{{#field:type}}
    {{^type:resolves_to_complex_return?}}
    {{^type:enum?}}
    {{type:cpp_type}} uarg_{{field:cpp_name}}{0};
    {{/type:enum?}}
    {{#type:enum?}}
    {{type:cpp_type}} uarg_{{field:cpp_name}}{static_cast<{{type:cpp_type}}>(0)};
    {{/type:enum?}}
    {{/type:resolves_to_complex_return?}}
    {{#type:resolves_to_complex_return?}}
    {{#function:stack_arguments?}}
    {{type:cpp_type}} uarg_{{field:cpp_name}};
    {{/function:stack_arguments?}}
    {{^function:stack_arguments?}}
    std::unique_ptr<{{type:cpp_type}}> uarg_{{field:cpp_name}} = std::make_unique<{{type:cpp_type}}>();
    {{/function:stack_arguments?}}
    {{/type:resolves_to_complex_return?}}
{{/field:type}}{{/function:args}}
    {{service:parent_service_cpp_name}}_{{#service:interaction?}}{{service:name}}_{{/service:interaction?}}{{function:cpp_name}}_pargs pargs() {
      {{service:parent_service_cpp_name}}_{{#service:interaction?}}{{service:name}}_{{/service:interaction?}}{{function:cpp_name}}_pargs args;
{{#function:args}}{{#field:type}}
      {{^type:resolves_to_complex_return?}}
      args.get<{{field:index}}>().value = &uarg_{{field:cpp_name}};
      {{/type:resolves_to_complex_return?}}
      {{#type:resolves_to_complex_return?}}
      {{#function:stack_arguments?}}
      args.get<{{field:index}}>().value = &uarg_{{field:cpp_name}};
      {{/function:stack_arguments?}}
      {{^function:stack_arguments?}}
      args.get<{{field:index}}>().value = uarg_{{field:cpp_name}}.get();
      {{/function:stack_arguments?}}
      {{/type:resolves_to_complex_return?}}
{{/field:type}}{{/function:args}}
      return args;
    }

    auto asTupleOfRefs() & {
      return std::tie(
{{#function:args}}{{#field:type}}
        {{^type:resolves_to_complex_return?}}
        std::as_const(uarg_{{field:cpp_name}}){{^last?}},{{/last?}}
        {{/type:resolves_to_complex_return?}}
        {{#type:resolves_to_complex_return?}}
        {{#function:stack_arguments?}}
        std::as_const(uarg_{{field:cpp_name}}){{^last?}},{{/last?}}
        {{/function:stack_arguments?}}
        {{^function:stack_arguments?}}
        std::as_const(*uarg_{{field:cpp_name}}){{^last?}},{{/last?}}
        {{/function:stack_arguments?}}
        {{/type:resolves_to_complex_return?}}
{{/field:type}}{{/function:args}}
      );
    }
  } args;

  auto ctxStack = apache::thrift::ContextStack::create(
      this->getEventHandlersSharedPtr(),
      this->getServiceName(),
      "{{service:parent_service_name}}.{{> common/function_name}}",
      serverRequest.requestContext());
{{#service:interaction?}}
  auto& iface = static_cast<apache::thrift::ServiceHandler<{{service:parent_service_cpp_name}}>::{{service:name}}If&>(*tile);
{{/service:interaction?}}
  try {
    auto pargs = args.pargs();
    deserializeRequest<ProtocolIn_>(
        pargs,
        "{{> common/function_name}}",
        apache::thrift::detail::ServerRequestHelper::compressedRequest(
            std::move(serverRequest))
            .uncompress(),
        ctxStack.get());
  } catch (...) {
{{#function:oneway?}}
    LOG(ERROR) << "exception in function {{> common/function_name}}: " << folly::exceptionStr(std::current_exception());
    apache::thrift::detail::ServerRequestHelper::eventBase(serverRequest)->runInEventBaseThread(
        [req = apache::thrift::detail::ServerRequestHelper::request(std::move(serverRequest))] {});
    return;
  }
  auto requestPileNotification = apache::thrift::detail::ServerRequestHelper::moveRequestPileNotification(serverRequest);
  auto concurrencyControllerNotification =
      apache::thrift::detail::ServerRequestHelper::moveConcurrencyControllerNotification(
          serverRequest);
  apache::thrift::HandlerCallbackBase::MethodNameInfo methodNameInfo{
      /* .serviceName =*/ this->getServiceName(),
      /* .definingServiceName =*/ "{{service:parent_service_name}}",
      /* .methodName =*/ "{{> common/function_name}}",
      /* .qualifiedMethodName =*/ "{{#if service:interaction?}}{{service:parent_service_name}}{{#else}}{{service:name}}{{/if service:interaction?}}.{{> common/function_name}}"};
  auto callback = std::make_unique<apache::thrift::HandlerCallbackOneWay>(
      apache::thrift::detail::ServerRequestHelper::request(std::move(serverRequest)),
      std::move(ctxStack),
      std::move(methodNameInfo),
      nullptr /* exceptionFuncPointer */,
      apache::thrift::detail::ServerRequestHelper::eventBase(serverRequest),
      apache::thrift::detail::ServerRequestHelper::executor(serverRequest),
      serverRequest.requestContext(),
      requestPileNotification,
      concurrencyControllerNotification,
      std::move(serverRequest.requestData()){{#function:in_or_creates_interaction?}},
      std::move(tile){{/function:in_or_creates_interaction?}});
{{/function:oneway?}}
{{^function:oneway?}}
    folly::exception_wrapper ew(std::current_exception());
    apache::thrift::detail::ap::process_handle_exn_deserialization<
        ProtocolOut_>(
        ew,
        apache::thrift::detail::ServerRequestHelper::request(std::move(serverRequest)),
            serverRequest.requestContext(),
        apache::thrift::detail::ServerRequestHelper::eventBase(serverRequest),
        "{{> common/function_name}}");
    return;
  }
  auto requestPileNotification =
      apache::thrift::detail::ServerRequestHelper::moveRequestPileNotification(
          serverRequest);
  auto concurrencyControllerNotification =
      apache::thrift::detail::ServerRequestHelper::moveConcurrencyControllerNotification(
          serverRequest);
  apache::thrift::HandlerCallbackBase::MethodNameInfo methodNameInfo{
      /* .serviceName =*/ this->getServiceName(),
      /* .definingServiceName =*/ "{{service:parent_service_name}}",
      /* .methodName =*/ "{{> common/function_name}}",
      /* .qualifiedMethodName =*/ "{{#if service:interaction?}}{{service:parent_service_name}}{{#else}}{{service:name}}{{/if service:interaction?}}.{{> common/function_name}}"};
  auto callback =
      apache::thrift::HandlerCallbackPtr<{{> types/return_type_server}}>::make(
          apache::thrift::detail::ServerRequestHelper::request(
              std::move(serverRequest)),
          std::move(ctxStack),
          std::move(methodNameInfo),
          return_{{function:prefixed_name}}<ProtocolIn_, ProtocolOut_>,
          throw_wrapped_{{#service:interaction?}}{{service:name}}_{{/service:interaction?}}{{function:cpp_name}}<ProtocolIn_, ProtocolOut_>,
          serverRequest.requestContext()->getProtoSeqId(),
          apache::thrift::detail::ServerRequestHelper::eventBase(serverRequest),
          {{^function:eb}}apache::thrift::detail::ServerRequestHelper::executor(serverRequest){{/function:eb}}{{#function:eb}}nullptr{{/function:eb}},
          serverRequest.requestContext(),
          requestPileNotification,
          concurrencyControllerNotification,
          std::move(serverRequest.requestData()){{#function:in_or_creates_interaction?}},
          std::move(tile){{/function:in_or_creates_interaction?}});
{{/function:oneway?}}
  const auto makeExecuteHandler = [&] {
    return [ifacePtr = {{^service:interaction?}}iface_{{/service:interaction?}}{{#service:interaction?}}&iface{{/service:interaction?}}](auto&& cb, ArgsState args) mutable {
      (void)args;
      ifacePtr->{{#function:eb}}async_eb{{/function:eb}}{{^function:eb}}async_tm{{/function:eb}}_{{function:cpp_name}}(std::move(cb){{> service_tcc/get_args_ref}});
    };
  };
#if FOLLY_HAS_COROUTINES
  if (apache::thrift::detail::shouldProcessServiceInterceptorsOnRequest(
          *callback)) {
    [](auto callback, auto executeHandler, ArgsState args)
        -> folly::coro::Task<void> {
      auto argRefs = args.asTupleOfRefs();
      co_await apache::thrift::detail::processServiceInterceptorsOnRequest(
          *callback,
          apache::thrift::detail::ServiceInterceptorOnRequestArguments(
              argRefs));
      executeHandler(std::move(callback), std::move(args));
    }(std::move(callback), makeExecuteHandler(), std::move(args))
               .scheduleOn(
                   apache::thrift::detail::ServerRequestHelper::executor(
                       serverRequest))
               .startInlineUnsafe();
  } else {
    makeExecuteHandler()(std::move(callback), std::move(args));
  }
#else
  makeExecuteHandler()(std::move(callback), std::move(args));
#endif // FOLLY_HAS_COROUTINES
}

{{^function:oneway?}}
template <class ProtocolIn_, class ProtocolOut_>
{{^function:cpp_recv_arg?}}
apache::thrift::SerializedResponse {{service:parent_service_cpp_name}}AsyncProcessor::return_{{function:prefixed_name}}(
    apache::thrift::ContextStack* ctx) {
{{/function:cpp_recv_arg?}}
{{#function:cpp_recv_arg?}}
{{^function:stream?}}{{^function:sink?}}
apache::thrift::SerializedResponse {{service:parent_service_cpp_name}}AsyncProcessor::return_{{function:prefixed_name}}(
    apache::thrift::ContextStack* ctx,
    {{function:cpp_return_type}} const& _return) {
{{/function:sink?}}{{/function:stream?}}
{{#function:stream?}}
apache::thrift::ResponseAndServerStreamFactory {{service:parent_service_cpp_name}}AsyncProcessor::return_{{function:prefixed_name}}(
    apache::thrift::ContextStack* ctx,
    folly::Executor::KeepAlive<> executor,
    {{function:cpp_return_type}}&& _return) {
{{/function:stream?}}
{{#function:sink?}}
std::pair<
    apache::thrift::SerializedResponse,
    apache::thrift::detail::SinkConsumerImpl>
{{service:parent_service_cpp_name}}AsyncProcessor::return_{{function:prefixed_name}}(
    apache::thrift::ContextStack* ctx,
    {{function:cpp_return_type}}&& _return,
    folly::Executor::KeepAlive<> executor) {
{{/function:sink?}}
{{/function:cpp_recv_arg?}}
  ProtocolOut_ prot;
{{#function:stream?}}
  {{service:parent_service_cpp_name}}_{{function:prefixed_name}}_presult::InitialResponsePResultType result;
  using StreamPResultType = {{service:parent_service_cpp_name}}_{{function:prefixed_name}}_presult::StreamPResultType;
{{#function:stream_has_first_response?}}
  result.get<0>().value = const_cast<{{function:cpp_return_type}}::ResponseType*>(&_return.response);
  result.setIsSet(0, true);
  auto& returnStream = _return.stream;
{{/function:stream_has_first_response?}}
{{^function:stream_has_first_response?}}
  auto& returnStream = _return;
{{/function:stream_has_first_response?}}
  auto encodedStream = apache::thrift::detail::ap::encode_server_stream<ProtocolOut_, StreamPResultType>(std::move(returnStream), std::move(executor));
  return {serializeResponse("{{> common/function_name}}", &prot, ctx, result), std::move(encodedStream)};
{{/function:stream?}}
{{#function:sink?}}
  {{service:parent_service_cpp_name}}_{{function:prefixed_name}}_presult::InitialResponsePResultType result;
  using SinkPResultType = {{service:parent_service_cpp_name}}_{{function:prefixed_name}}_presult::SinkPResultType;
  using FinalResponsePResultType =
      {{service:parent_service_cpp_name}}_{{function:prefixed_name}}_presult::FinalResponsePResultType;
{{#function:sink_has_first_response?}}
  result.get<0>().value = &_return.response;
  result.setIsSet(0, true);
{{/function:sink_has_first_response?}}
  auto sinkConsumerImpl = apache::thrift::detail::ap::toSinkConsumerImpl<
      ProtocolIn_,
      ProtocolOut_,
      SinkPResultType,
      FinalResponsePResultType>({{#function:sink_has_first_response?}}std::move(_return.sinkConsumer),{{/function:sink_has_first_response?}}{{^function:sink_has_first_response?}}std::move(_return), {{/function:sink_has_first_response?}}std::move(executor));

  return {serializeResponse("{{> common/function_name}}", &prot, ctx, result), std::move(sinkConsumerImpl)};
{{/function:sink?}}
{{^function:stream?}}{{^function:sink?}}
  ::{{service:qualified_namespace}}::{{service:parent_service_cpp_name}}_{{function:prefixed_name}}_presult result;
{{^type:void?}}
  result.get<0>().value = const_cast<{{function:cpp_return_type}}*>(&_return);
  result.setIsSet(0, true);
{{/type:void?}}
  return serializeResponse("{{> common/function_name}}", &prot, ctx, result);
{{/function:sink?}}{{/function:stream?}}
}

template <class ProtocolIn_, class ProtocolOut_>
void {{service:parent_service_cpp_name}}AsyncProcessor::throw_wrapped_{{function:prefixed_name}}(
    apache::thrift::ResponseChannelRequest::UniquePtr req,
    [[maybe_unused]] int32_t protoSeqId,
    apache::thrift::ContextStack* ctx,
    folly::exception_wrapper ew,
    apache::thrift::Cpp2RequestContext* reqCtx) {
  if (!ew) {
    return;
  }
{{#if function:exceptions?}}
  ::{{service:qualified_namespace}}::{{service:parent_service_cpp_name}}_{{function:prefixed_name}}_presult{{#if (or function:sink? function:stream?)}}::InitialResponsePResultType{{/if}} result;
  constexpr bool kHasReturnType = {{#if function:initial_response?}}true{{#else}}false{{/if}};
  if (!::apache::thrift::detail::ap::insert_exn<kHasReturnType>(result, ew, [&]<typename Ex>(Ex&){
    if (ctx) {
      ctx->userExceptionWrapped(true, ew);
    }
    ::apache::thrift::util::appendExceptionToHeader(ew, *reqCtx);
    ::apache::thrift::util::appendErrorClassificationToHeader<Ex>(ew, *reqCtx);
  })) {
{{#else}}
  {
{{/if function:exceptions?}}
    apache::thrift::detail::ap::process_throw_wrapped_handler_error<
        ProtocolOut_>(ew, std::move(req), reqCtx, ctx, "{{> common/function_name}}");
    return;
  }
{{#function:exceptions?}}
  ProtocolOut_ prot;
  auto response = serializeResponse("{{> common/function_name}}", &prot, ctx, result);
  auto payload = std::move(response).extractPayload(
      req->includeEnvelope(),
      prot.protocolType(),
      protoSeqId,
      apache::thrift::MessageType::T_REPLY,
      "{{> common/function_name}}");
  payload.transform(reqCtx->getHeader()->getWriteTransforms());
  {{#if (not (or function:sink? function:stream?))}}
  return req->sendReply(std::move(payload));
  {{#else if function:stream?}}
  req->sendStreamReply(std::move(payload), apache::thrift::detail::ServerStreamFactory{nullptr});
  {{#else}}
  req->sendSinkReply(std::move(payload), apache::thrift::detail::SinkConsumerImpl{});
{{/if (not (or function:sink? function:stream?))}}
{{/function:exceptions?}}
}
{{/function:oneway?}}
//
// End of Method '{{function:name}}'
//

{{/function:starts_interaction?}}{{/function:return_type}}{{/service:functions}}{{#service:functions?}}
//
// End of Service Methods
//
{{/service:functions?}}
